
const dynamicCall = require('./dynamicCall');
const fs = require('fs/promises');
const fs2 = require('fs');
const path = require('path');
const http = require("http");
const { Sequelize, DataTypes } = require('sequelize');

Date.prototype.toJSON = function () {
    var timezoneOffsetInHours = -(this.getTimezoneOffset() / 60);
    var correctedDate = new Date(this.getFullYear(), this.getMonth(),
        this.getDate(), this.getHours(), this.getMinutes(), this.getSeconds(),
        this.getMilliseconds());
    correctedDate.setHours(this.getHours() + timezoneOffsetInHours);
    var iso = correctedDate.toISOString().replace('Z', '').replace('T',' ').replace(/\.[\d]+/g,'');
    return iso;
};


const mysql = new Sequelize('demo1', 'root', '123456', {
    dialect: 'mysql', // sqlite3 安装过程，npm包出现 403. 所以暂时用mysql
    host: 'localhost',
    port: 3306,
    waitForConnections: true,
    connectionLimit: 10,
})

const ExecLog = mysql.define('execlogs', {
    id: {
        type: DataTypes.BIGINT,
        primaryKey: true,
        autoIncrement: true,
    },
    call_string: {
        type: DataTypes.STRING
    },
    result: {
        type: DataTypes.STRING
    },
});

// 创建服务器实例
const server = http.createServer();
const ContentType = "Content-type";
const octetStream = "application/octet-stream";
const JsonMimeType = "application/json; charset=utf-8";
const HtmlMimeType = "text/html; charset=utf-8";
const font_mt_header = {[ContentType]:octetStream}
const MT = {
    css: {
        headers: {
            [ContentType]: "text/plain; charset=utf-8"
        }
    },
    js: {
        headers: {
            [ContentType]: "text/javascript; charset=utf-8"
        }
    },
    woff: {
        headers: {...font_mt_header}
    },
    ttf: {
        headers: {...font_mt_header}
    }
}

// 判断url进行基础实现，不使用mvc框架
server.on("request", async (req, res) => {
    const url = req.url; // 此次请求的 路径
    const method = req.method; // 此次请求的 方法 
    // console.log(`request url:${url},method:${method}`)
    let response = "";
    let error404 = false;
    res.setHeader(ContentType, JsonMimeType);
    if (url && url.startsWith('/static/') && url.indexOf('.') != -1) {
        const fileExtension = url.substring(url.lastIndexOf('.') + 1).toLowerCase();
        let mt = MT[fileExtension] || {}
        if (mt && mt.headers) { 
            for (let hk in mt.headers) {
                res.setHeader(hk, mt.headers[hk]);
            }
            let fp = path.join(__dirname, 'static', url.replace('/static/', ''));
            try {
                if (mt.headers[ContentType] === octetStream) { 
                    res.setHeader('Content-Disposition', `attachment; filename="${path.basename(fp)}"`, )
                    const readStream = fs2.createReadStream(fp); 
                    readStream.on('open', () => { 
                        readStream.pipe(res);
                    })
                    readStream.on('error', (err) => { 
                        res.end(err)
                    })
                    return;
                }
                response = await fs.readFile(fp, 'utf-8');
            } catch (e) {
                console.log(e)
                error404 = true;
            }
            
        }
    } else if (url === '/') {
        response = await fs.readFile(path.join(__dirname, 'views/index.html'), 'utf-8');
        res.setHeader(ContentType, HtmlMimeType);
    } else if (url === '/call_') {
        let postData = ''
        req.on('data', (data) => {
            postData += data
        })

        req.on('end', async () => {
            postData = postData || '{}'
            let params = null;
            try {
                params = JSON.parse(postData);
                 // console.log('postData',postData)
            } catch (ex) { 
                response = { code: 100, message:  `参数错误：${ex}` };
            }
            params.call_string = params.call_string.trim();
            // 第一部分模块对象名，第二部分方法名，第三部分参数列表
            let regex = /^(\w+)\.(\w+)\((.*)\)$/;
            const matchResult = params.call_string.match(regex); 

            const [_, objName, methodName, rawArgsStr] =
                (matchResult && typeof matchResult[Symbol.iterator] === 'function' ? matchResult : []);
            if (objName) {
                let ms = await dynamicCall.loadScriptModules()
                let m = ms[objName];
                if (m && m.exports) {
                    try {
                        let result = dynamicCall.dynamicCall(m.exports, params.call_string)
                        response = { code: 0, data: result, message: '操作成功' };
                        // 持久化到本地
                        await ExecLog.create({
                            call_string: params.call_string,
                            // 目前返回的是字符串，若返回对象，持久化对象json字符串
                            result: typeof result === 'string' || !result ? result : JSON.stringify(result),
                        })
                    } catch (e) {
                        response = { code: 100, message: `执行出错：${e}` };
                    }
                } else {
                    response = { code: 100, message:  `模块${objName}，不存在` };
                }
            }
            response = response || { code: 100, message: `参数异常:${params.call_string}` };
            response = JSON.stringify(response);
            res.end(response);
        });
        return;
    } else if ('/exeLogs' === url) {
        try {
            // 简单查看最新20条执行历史
            const logs = await ExecLog.findAll({limit : 20, order: [["createdAt", "desc"]], });
            response = { code: 0, data: logs, message: `操作成功` };
        } catch (error) {
            response = { code: 100, message: `操作失败：${error}` };
        }
        response = JSON.stringify(response);
    } else {
        error404 = true;
    }
    if (error404) {
        response = '<h1>404</h1>';
        res.setHeader(ContentType, HtmlMimeType);
    }
    res.end(response);
});

// 启动服务器
server.listen(8080, () => { // 8080 是服务开启在本机的 8080 端口
    console.log("服务器启动成功！ http://localhost:8080/");
});